From d9190d9862f5fd8addaea6dc39a80c85e45dee38 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Tue, 10 Mar 2015 13:12:47 -0400 Subject: [PATCH] Add dedicated metadata for adding -l/-L flags Deprecates the rustc-args metadata keyword in favor of rustc-link-lib and rustc-link-search, which are more precise and allows for easy, correct handling of spaces in pathnames. Closes #1015 --- src/cargo/ops/cargo_compile.rs | 39 +++++++---- src/cargo/ops/cargo_rustc/custom_build.rs | 4 ++ src/cargo/util/config.rs | 8 +++ src/doc/build-script.md | 19 +++-- tests/test_cargo_compile_custom_build.rs | 84 +++++++++++++++++++++++ 5 files changed, 137 insertions(+), 17 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index e848ce9f1..f9fd404a0 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -26,7 +26,7 @@ use std::collections::HashMap; use std::default::Default; use std::num::ToPrimitive; use std::os; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use core::registry::PackageRegistry; @@ -34,7 +34,7 @@ use core::{Source, SourceId, PackageSet, Package, Target, PackageId}; use core::resolver::Method; use ops::{self, BuildOutput, ExecEngine}; use sources::{PathSource}; -use util::config::Config; +use util::config::{ConfigValue, Config}; use util::{CargoResult, internal, human, ChainError, profile}; /// Contains informations about how a package should be compiled. @@ -221,6 +221,7 @@ fn scrape_build_config(config: &Config, fn scrape_target_config(config: &Config, triple: &str) -> CargoResult { + let key = format!("target.{}", triple); let ar = try!(config.get_string(&format!("{}.ar", key))); let linker = try!(config.get_string(&format!("{}.linker", key))); @@ -246,16 +247,30 @@ fn scrape_target_config(config: &Config, triple: &str) let table = try!(config.get_table(&key)).unwrap().0; for (k, _) in table.into_iter() { let key = format!("{}.{}", key, k); - let (v, path) = try!(config.get_string(&key)).unwrap(); - if k == "rustc-flags" { - let whence = format!("in `{}` (in {:?})", key, path); - let (paths, links) = try!( - BuildOutput::parse_rustc_flags(&v, &whence) - ); - output.library_paths.extend(paths.into_iter()); - output.library_links.extend(links.into_iter()); - } else { - output.metadata.push((k, v)); + match try!(config.get(&key)).unwrap() { + ConfigValue::String(v, path) => { + if k == "rustc-flags" { + let whence = format!("in `{}` (in {:?})", key, path); + let (paths, links) = try!( + BuildOutput::parse_rustc_flags(&v, &whence) + ); + output.library_paths.extend(paths.into_iter()); + output.library_links.extend(links.into_iter()); + } else { + output.metadata.push((k, v)); + } + }, + ConfigValue::List(a, p) => { + if k == "rustc-link-lib" { + output.library_links.extend(a.into_iter().map(|(v, _)| v)); + } else if k == "rustc-link-search" { + output.library_paths.extend(a.into_iter().map(|(v, _)| PathBuf::new(&v))); + } else { + try!(config.expected("string", &k, ConfigValue::List(a, p))); + } + }, + // technically could be a list too, but that's the exception to the rule... + cv => { try!(config.expected("string", &k, cv)); } } } ret.overrides.insert(lib_name, output); diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index 76ee30fb8..48bb6a285 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -287,6 +287,10 @@ impl BuildOutput { ); library_links.extend(links.into_iter()); library_paths.extend(libs.into_iter()); + } else if key == "rustc-link-lib" { + library_links.push(value.to_string()); + } else if key == "rustc-link-search" { + library_paths.push(PathBuf::new(&value)); } else { metadata.push((key.to_string(), value.to_string())) } diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 7677da2da..b633e9366 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -132,6 +132,14 @@ impl<'a> Config<'a> { } } + pub fn get_list(&self, key: &str) -> CargoResult, PathBuf)>> { + match try!(self.get(key)) { + Some(CV::List(i, path)) => Ok(Some((i, path))), + Some(val) => self.expected("list", key, val), + None => Ok(None), + } + } + pub fn get_table(&self, key: &str) -> CargoResult, PathBuf)>> { match try!(self.get(key)) { diff --git a/src/doc/build-script.md b/src/doc/build-script.md index 6d97f578c..0ac897e97 100644 --- a/src/doc/build-script.md +++ b/src/doc/build-script.md @@ -77,14 +77,20 @@ are interpreted by Cargo and must be of the form `key=value`. Example output: ```notrust -cargo:rustc-flags=-l static=foo -L native=/path/to/foo +cargo:rustc-link-lib=static=foo +cargo:rustc-link-search=native=/path/to/foo cargo:root=/path/to/foo cargo:libdir=/path/to/foo/lib cargo:include=/path/to/foo/include ``` +The `rustc-link-lib` key indicates that Cargo should pass a `-l` option to +rustc. Similarly, `rustc-link-search` indicates that Cargo should pass a `-L` +option. + The `rustc-flags` key is special and indicates the flags that Cargo will -pass to Rustc. Currently only `-l` and `-L` are accepted. +pass to Rustc. Currently only `-l` and `-L` are accepted. Using +`rustc-link-lib` and `rustc-link-search` is more robust. Any other element is a user-defined metadata that will be passed to dependencies. More information about this can be found in the [`links`][links] @@ -157,7 +163,8 @@ Cargo [configuration location](config.html). ```toml [target.x86_64-unknown-linux-gnu.foo] -rustc-flags = "-L /path/to/foo -l foo" +rustc-link-search = ["/path/to/foo"] +rustc-link-lib = ["foo"] root = "/path/to/foo" key = "value" ``` @@ -165,7 +172,8 @@ key = "value" This section states that for the target `x86_64-unknown-linux-gnu` the library named `foo` has the metadata specified. This metadata is the same as the metadata generated as if the build script had run, providing a number of -key/value pairs where the `rustc-flags` key is slightly special. +key/value pairs where the `rustc-flags`, `rustc-link-search`, and +`rustc-link-lib` keys are slightly special. With this configuration, if a package declares that it links to `foo` then the build script will **not** be compiled or run, and the metadata specified will @@ -313,7 +321,8 @@ fn main() { .current_dir(&Path::new(&out_dir)) .status().unwrap(); - println!("cargo:rustc-flags=-L native={} -l static=hello", out_dir); + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=static=hello"); } ``` diff --git a/tests/test_cargo_compile_custom_build.rs b/tests/test_cargo_compile_custom_build.rs index 03d0dd142..20b4557ef 100644 --- a/tests/test_cargo_compile_custom_build.rs +++ b/tests/test_cargo_compile_custom_build.rs @@ -574,6 +574,64 @@ test!(propagation_of_l_flags { ", compiling = COMPILING, running = RUNNING).as_slice())); }); +test!(propagation_of_l_flags_new { + let (_, target) = ::cargo::ops::rustc_version().unwrap(); + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.0" + authors = [] + [dependencies.a] + path = "a" + "#) + .file("src/lib.rs", "") + .file("a/Cargo.toml", r#" + [project] + name = "a" + version = "0.5.0" + authors = [] + links = "bar" + build = "build.rs" + + [dependencies.b] + path = "../b" + "#) + .file("a/src/lib.rs", "") + .file("a/build.rs", r#" + fn main() { + println!("cargo:rustc-link-search=bar"); + } + "#) + .file("b/Cargo.toml", r#" + [project] + name = "b" + version = "0.5.0" + authors = [] + links = "foo" + build = "build.rs" + "#) + .file("b/src/lib.rs", "") + .file("b/build.rs", "bad file") + .file(".cargo/config", format!(r#" + [target.{}.foo] + rustc-link-search = ["foo"] + "#, target).as_slice()); + + assert_that(p.cargo_process("build").arg("-v").arg("-j1"), + execs().with_status(0) + .with_stdout(format!("\ +[..] +[..] +[..] +[..] +{running} `[..]a-[..]build-script-build[..]` +{running} `rustc [..] --crate-name a [..]-L bar[..]-L foo[..]` +{compiling} foo v0.5.0 (file://[..]) +{running} `rustc [..] --crate-name foo [..] -L bar -L foo` +", compiling = COMPILING, running = RUNNING).as_slice())); +}); + test!(build_deps_simple { let p = project("foo") .file("Cargo.toml", r#" @@ -785,6 +843,32 @@ test!(output_separate_lines { ", compiling = COMPILING, running = RUNNING).as_slice())); }); +test!(output_separate_lines_new { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.0" + authors = [] + build = "build.rs" + "#) + .file("src/lib.rs", "") + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-link-search=foo"); + println!("cargo:rustc-link-lib=static=foo"); + } + "#); + assert_that(p.cargo_process("build").arg("-v"), + execs().with_status(101) + .with_stdout(format!("\ +{compiling} foo v0.5.0 (file://[..]) +{running} `rustc build.rs [..]` +{running} `[..]foo-[..]build-script-build[..]` +{running} `rustc [..] --crate-name foo [..] -L foo -l static=foo` +", compiling = COMPILING, running = RUNNING).as_slice())); +}); + #[cfg(not(windows))] // FIXME(#867) test!(code_generation { let p = project("foo") -- 2.30.2